//
// Created by Gorun on 2022/5/3.
//

#ifndef LOXPP_STMT_H
#define LOXPP_STMT_H

#include <any>
#include <memory>
#include <utility>
#include <vector>

#include "Token.h"
#include "Expr.h"

using namespace std;

/*
 * Current statement structure:
 * program        → declaration* EOF ;
   declaration    → varDecl
                  | funDecl
                  | classDecl
                  | statement ;
   statement      → exprStmt
                  | ifStmt
                  | printStmt
                  | inputStmt
                  | whileStmt
                  | forStmt
                  | block
                  | returnStmt;
   block          → "{" declaration* "}" ;
   varDecl        → "var" IDENTIFIER ( "=" expression )? ";" ;
   exprStmt       → expression ";" ;
   printStmt      → "print" expression ";" ;
   ifStmt         → "if" "(" expression ")" statement
                  ( "else" statement)? ;
   funDecl        → "fun" function ;
   function       → IDENTIFIER "(" parameters? ")" block ;
   parameters     → IDENTIFIER ( "," IDENTIFIER )* ;
   returnStmt     → "return" expression? ";" ;
   classDecl      → "class" IDENTIFIER ( "<" IDENTIFIER )?
                    "{" function* "}" ;
 * */

struct Expression; //Expr is occupied by Expr itself :)
struct Print;
struct Input;
struct Block;
struct Var;
struct If;
struct While;
struct Function;
struct Return;
struct Class;

//struct For;
//No For node is still acceptable, because that:
/*
 * for (var i = 0; i<10; i=i+1) {
 *      print i;
 * }
 */
//Equals to :
/*
 * {
 *      var i = 0;
 *      while (i < 10) {
 *          print i;
 *          i=i+1;
 *      }
 * }
 */
//Thus, we can convert a for statement into a while node in the parser.

//StmtVisitor is a class of method that
//every visitor should implement
class StmtVisitor {
public:
    virtual any visitExpressionStmt(shared_ptr<Expression> stmt) = 0;
    virtual any visitPrintStmt(shared_ptr<Print> stmt) = 0;
    virtual any visitInputStmt(shared_ptr<Input> stmt) = 0;
    virtual any visitBlockStmt(shared_ptr<Block> stmt) = 0;
    virtual any visitVarStmt(shared_ptr<Var> stmt) = 0;
    virtual any visitIfStmt(shared_ptr<If> stmt) = 0;
    virtual any visitWhileStmt(shared_ptr<While> stmt) = 0;
    virtual any visitFunctionStmt(shared_ptr<Function> stmt) = 0;
    virtual any visitReturnStmt(shared_ptr<Return> stmt) = 0;
    virtual any visitClassStmt(shared_ptr<Class> stmt) = 0;
};

struct Stmt {
    virtual any accept(StmtVisitor& visitor) = 0;
};

struct Expression: Stmt, public enable_shared_from_this<Expression> {
    explicit Expression(shared_ptr<Expr> expression)
    : expression{move(expression)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitExpressionStmt(shared_from_this());
    }

    const shared_ptr<Expr> expression;
};

struct Print: Stmt, public enable_shared_from_this<Print> {
    Print(shared_ptr<Expr> expression, bool ln)
    : expression{move(expression)}, ln{ln}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitPrintStmt(shared_from_this());
    }

    const shared_ptr<Expr> expression;
    const bool ln;
};

struct Input: Stmt, public enable_shared_from_this<Input> {
    explicit Input(Token variable)
    : variable{move(variable)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitInputStmt(shared_from_this());
    }

    const Token variable;
};

struct Block: Stmt, public enable_shared_from_this<Block> {
    explicit Block(vector<shared_ptr<Stmt>> statements)
    : statements{move(statements)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitBlockStmt(shared_from_this());
    }

    const vector<shared_ptr<Stmt>> statements;
};

struct Var: Stmt, public enable_shared_from_this<Var> {
    Var(Token name, shared_ptr<Expr> initializer)
    : name{move(name)}, initializer{move(initializer)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitVarStmt(shared_from_this());
    }

    const Token name;
    const shared_ptr<Expr> initializer;
};

struct If: Stmt, public enable_shared_from_this<If> {
    If(shared_ptr<Expr> condition,
       shared_ptr<Stmt> thenBranch, shared_ptr<Stmt> elseBranch)
    : condition{move(condition)},
    thenBranch{move(thenBranch)}, elseBranch{move(elseBranch)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitIfStmt(shared_from_this());
    }

    const shared_ptr<Expr> condition;
    const shared_ptr<Stmt> thenBranch;
    const shared_ptr<Stmt> elseBranch;
};

struct While: Stmt, public enable_shared_from_this<While> {
    While(shared_ptr<Expr> condition, shared_ptr<Stmt> body)
    : condition{move(condition)}, body{move(body)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitWhileStmt(shared_from_this());
    }

    shared_ptr<Expr> condition;
    shared_ptr<Stmt> body;
};

struct Function: Stmt, public enable_shared_from_this<Function> {
    Function(Token name, vector<Token> params, vector<shared_ptr<Stmt>> body)
    : name{move(name)}, params{move(params)}, body{move(body)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitFunctionStmt(shared_from_this());
    }

    const Token name;
    const vector<Token> params;
    const vector<shared_ptr<Stmt>> body;
};

struct Return: Stmt, public enable_shared_from_this<Return> {
    Return(Token keyword, shared_ptr<Expr> value)
    : keyword{move(keyword)}, value{move(value)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitReturnStmt(shared_from_this());
    }

    const Token keyword;
    const shared_ptr<Expr> value;
};

struct Class: Stmt, public enable_shared_from_this<Class> {
    Class(Token name, shared_ptr<Variable> superclass, vector<shared_ptr<Function>> methods)
    : name{move(name)}, superclass{move(superclass)}, methods{move(methods)}
    {}

    any accept(StmtVisitor &visitor) override {
        return visitor.visitClassStmt(shared_from_this());
    }

    const Token name;
    const shared_ptr<Variable> superclass;
    const vector<shared_ptr<Function>> methods;
};

#endif //LOXPP_STMT_H
